package main

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	"gopkg.in/yaml.v3"
)

type MysqlConfig struct {
	Address  string `yaml:"address"`
	Username string `yaml:"username"`
	Password string `yaml:"password"`
	Database string `yaml:"database"`
	Params   string `yaml:"params"`
	TestConn bool   `yaml:"testConn"`
}

type GoFmtConfig struct {
	Cmd  string   `yaml:"cmd"`
	Args []string `yaml:"args"`
}

type CommonConfig struct {
	TablePrefixes []string          `yaml:"tablePrefixes"`
	ColumnTypes   map[string]string `yaml:"columnTypes"`
	GoFmt         *GoFmtConfig      `yaml:"goFmt"`
}

type DaoConfig struct {
	TemplateName string         `yaml:"templateName"`
	Imports      []string       `yaml:"imports"`
	DaoName      string         `yaml:"daoName"`
	ModelName    string         `yaml:"modelName"`
	ImportModel  bool           `yaml:"importModel"`
	Vars         map[string]any `yaml:"vars"`
}

type BaseModelConfig struct {
	TypeName string   `yaml:"typeName"`
	Fields   []string `yaml:"fields"`
}

type SoftDeleteConfig struct {
	ColumnName string `yaml:"columnName"`
	TypeName   string `yaml:"typeName"`
}

type ModelConfig struct {
	TemplateName string            `yaml:"templateName"`
	BaseModel    *BaseModelConfig  `yaml:"baseModel"`
	SoftDelete   *SoftDeleteConfig `yaml:"softDelete"`
}

type Config struct {
	Mysql  *MysqlConfig  `yaml:"mysql"`
	Common *CommonConfig `yaml:"common"`
	Dao    *DaoConfig    `yaml:"dao"`
	Model  *ModelConfig  `yaml:"model"`
}

func getConfigPath() string {
	customConfig := os.Getenv("MYBG_CONFIG")
	if customConfig != "" {
		return customConfig
	}

	configDir, err := os.UserConfigDir()
	if err != nil {
		fatalf("user config dir: %v\n", err)
	}

	return filepath.Join(configDir, appName, "config.yaml")
}

func loadConfig(fname string) (*Config, error) {
	data, err := os.ReadFile(fname)
	if err != nil {
		return nil, err
	}
	var config Config
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}
	return &config, nil
}

var (
	appConfig_ *Config
)

func getAppConfig() *Config {
	if appConfig_ != nil {
		return appConfig_
	}
	c, err := loadConfig(getConfigPath())
	if err != nil {
		fatalf("loadConfig: %v\n", err)
	}
	appConfig_ = c
	return c
}

func defaultConfig() *Config {
	return &Config{
		Mysql: &MysqlConfig{
			Address:  "localhost:3306",
			Username: "username",
			Password: "password",
			Database: "database",
			Params:   "charset=utf8mb4&loc=Local",
		},
	}
}

func configCommand() *cobra.Command {
	return &cobra.Command{
		Use:   "config",
		Short: "Print configuration",
		Args:  cobra.NoArgs,
		Run: func(cmd *cobra.Command, args []string) {
			newConfig := false
			fname := getConfigPath()
			config, err := loadConfig(fname)
			if err != nil {
				if !os.IsNotExist(err) {
					fatalf("loadConfig: %v\n", err)
				}

				config = defaultConfig()
				newConfig = true
			}
			var buf bytes.Buffer
			enc := yaml.NewEncoder(&buf)
			enc.SetIndent(2)
			enc.Encode(config)
			if newConfig {
				_ = os.MkdirAll(filepath.Dir(fname), 0700)
				err := os.WriteFile(fname, buf.Bytes(), 0600)
				if err != nil {
					fatalf("cannot generate configuration file: %v\n", err)
				}
				fmt.Printf("generate default configuration to %s\n", fname)
			} else {
				fmt.Printf("# Configuration file: %s\n", fname)
				fmt.Println(buf.String())
			}
		},
	}
}
